﻿using System;
using System.Linq;
using System.Text;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using T.FAS.Runtime.Metadata.Entity;
using DataTypeUtil = T.FAS.Runtime.Metadata.Entity.DataModel.DataTypeUtil;

namespace T.FAS.Runtime.DataModel.Dao
{
    /// <summary>
    /// 元数据验证接口
    /// </summary>
    public class DataObjectValidator : IMetaDataDesignValidator, IMetaDataRuntimeValidator
    {
        public string MetaDataType = DataModelConst.DOMetaType;
        public string MetaDataTypeCN = DataModelConst.DOMetaName;

        public bool CreateMetadata(IMetaDataContent metaDataContent)
        {
            var Assert = GetEntityValidateResult(metaDataContent);
            Assert.FailIfErrorResult();
            return Assert.GetAssertResult().Result;
        }

        public bool DeleteMetadata(IMetaDataContent metaDataContent)
        {
            FASAssert Assert = new FASAssert();
            Assert.IsNotNull(metaDataContent.MetaDataID, $"数据库删除时未指定MetaDataID");
            Assert.FailIfErrorResult();
            return Assert.GetAssertResult().Result;
        }

        public bool GetMetadata(IMetaDataContent metaDataContent)
        {
            var Assert = GetEntityValidateResult(metaDataContent, CRUDType.Retrive);
            Assert.FailIfErrorResult();
            return Assert.GetAssertResult().Result;
        }

        public bool UpdateMetadata(IMetaDataContent metaDataContent)
        {
            //因为目前更新采用先删后加的方式，所以采用相同校验规则
            return CreateMetadata(metaDataContent);
        }

        public bool GetMetadataRTP(IMetaDataContent metaDataContent)
        {
            //目前运行时获取元数据跟设计时一致
            return GetMetadata(metaDataContent);
        }

        private FASAssert GetEntityValidateResult(IMetaDataContent metaDataContent, CRUDType crudType = CRUDType.Create)
        {
            FASAssert Assert = new FASAssert();
            DataObject Meta = metaDataContent as DataObject;
            Assert.IsNotNull(Meta, $"请检查IMetaDataContent参数");
            Assert.FailIfErrorResult(); //如果为空引用直接抛出异常
            if (!string.IsNullOrWhiteSpace(metaDataContent?.MetaData?.MetaTypeID))
                MetaDataType = metaDataContent?.MetaData?.MetaTypeID;
            var unifyCode = $"元数据类型{MetaDataType}的元数据Code:{Meta.MetaDataCode},ID:{Meta.MetaDataID}";

            /***********通用系统字段校验**********/
            StringBuilder errmsg = new StringBuilder();
            //MetaDataID,MetaDataCode,MetaDataName等字段校验
            var systemFieldsCheckResult = EntityCheckTools.CheckSystemFields(Meta);
            Assert.IsTrue(systemFieldsCheckResult.Result, systemFieldsCheckResult.ErrorMsg);

            if (crudType == CRUDType.Create)
            {
                var MetaDataInfo = metaDataContent.MetaData;
                Assert.IsNotNull(MetaDataInfo, $"{unifyCode}的系统字段:{nameof(metaDataContent.MetaData)}不能为空!");
                //租户ID验证
                Assert.IsNotNull(MetaDataInfo.TenantId, $"{unifyCode}的系统字段:{nameof(MetaDataInfo.TenantId)}不能为空!");
            }

            //DO通用业务逻辑校验
            var BusinessAssert = DataObjectBusinessValidator.GetEntityValidateResult(Meta, crudType);
            BusinessAssert.FailIfErrorResult();

            return Assert;
        }
    }

    /// <summary>
    /// DataObject业务逻辑校验类
    /// </summary>
    public class DataObjectBusinessValidator
    {
        private static readonly DatabaseLogic s_databaseLogic = new DatabaseLogic();
        //DO依赖DB校验
        private static readonly DatabaseValidator s_databaseValidator = new DatabaseValidator();
        private static readonly DataObjectLogic s_dataObjectLogic = new DataObjectLogic();

        public static FASAssert GetEntityValidateResult(DataObject dataObject, CRUDType crudType = CRUDType.Create)
        {
            FASAssert assert = new FASAssert();
            var errSummary = $"元数据类型{DataModelConst.DOMetaType}的元数据Code:{dataObject.Code},ID:{dataObject.ID}";
            if (crudType == CRUDType.Create)
            {
                try
                {
                    var db = s_databaseLogic.GetDatabaseByID(dataObject.DatabaseID);
                    assert.IsNotNull(db, $"{errSummary}所属的DB:{dataObject.DatabaseID}元数据实例不存在");
                }
                catch (Exception e)
                {
                    throw new FasException(DataModelErrorCode.DO.ValidError, e.Message, e);
                }
            }

            /*********************业务校验***********************/
            /*********************DO校验***********************/
            assert.IsNotNull(dataObject.LogicTableName, $"{errSummary}的物理表字段{nameof(dataObject.LogicTableName)}字段不能为空！");
            //物理表名唯一性校验
            if (crudType == CRUDType.Create || crudType == CRUDType.Update)
            {
                var dao = s_dataObjectLogic.DataObjectDao;
                var doid = dataObject.ID ?? dataObject.MetaDataID;
                var dbid = dataObject.DatabaseID;
                assert.IsNotNull(doid, $"{errSummary}的主键id不能为空！");
            }

            assert.IsNotNull(dataObject.DatabaseID, $"{errSummary}的所属数据库ID {nameof(dataObject.DatabaseID)}字段不能为空！");
            assert.IsFalse(dataObject.IsMainData && dataObject.IsReference, $"{errSummary}不能同时作为主数据DO&引用DO");
            assert.IsTrue(!dataObject.IsReference || !string.IsNullOrEmpty(dataObject.RefDataObjectID), $"{errSummary}类型为引用DO时，RefDataObjectID不能为空");
            /*********************Column校验***********************/
            assert.IsTrue(dataObject.Columns != null && dataObject.Columns.Count > 0, $"{errSummary}的列集合不能为空！");
            var pkCols = dataObject.Columns.Where(col => col.IsPkColumn).ToList();
            assert.IsTrue(pkCols.Count == 1, $"{errSummary}的主键列只能有一个");
            /*********************Column校验===列信息校验***********************/
            foreach (var column in dataObject.Columns)
            {
                assert.IsFalse(EntityCheckTools.KeyWords.Contains(column.ColumnName), $"{errSummary}的列:{column.ColumnName}不能使用FAS关键词做列名");
                assert.IsNotNull(column.ColumnName, $"{errSummary}的列{nameof(column.ColumnName)}不能为空");
                assert.IsNotNull(column.DisplayName, $"{errSummary}的列:{column.ColumnName}的{nameof(column.DisplayName)}不能为空");
                assert.IsNotNull(column.ApplicationDataType, $"{errSummary}的列:{column.ColumnName}的{nameof(column.ApplicationDataType)}不能为空");
                assert.IsNotNull(column.DataType, $"{errSummary}的列:{column.ColumnName}的{nameof(column.DataType)}不能为空");

                assert.IsTrue(!column.IsFkColumn || !string.IsNullOrEmpty(column.RelatedDataObjectID),
                    $"{errSummary}的列:{column.ColumnName}为外键列，需要满足IsFkColumn:true,RelatedDataObjectID不为空的条件");

                if (!column.IsFkColumn)
                {
                    assert.IsTrue(DataTypeUtil.IsMatch(column.ApplicationDataType, column.DataType),
                        $"{errSummary}的列:{column.ColumnName}的数据库类型{column.DataType}与应用程序类型{column.ApplicationDataType}不匹配");
                }
                /***ToDO:DO.Column的类型校验，例如 VChar长度最大为xxx***/
            }

            /*********************Column校验===外键列&DOR联动校验***********************/
            var fkColumns = dataObject.Columns.FindAll(col => col.IsFkColumn);
            var dors = dataObject.DataObjectRelations;
            if (fkColumns.Count > 0)
            {
                assert.IsTrue(dors != null && dors.Count > 0, $"{errSummary}引用其它DO，但是DOR为空！");
                assert.IsTrue(fkColumns.Count == dors.Count, $"{errSummary}的外键列的数量必须跟DoRs数量相等");
                foreach (var fkCol in fkColumns)
                {
                    assert.IsTrue(fkCol.IsFkColumn, $"{errSummary}的外键列:{fkCol.ColumnName}的IsFkColumn属性必须为true");
                    assert.IsFalse(string.IsNullOrEmpty(fkCol.RelatedDataObjectID), $"{errSummary}的外键列{fkCol.ColumnName}的RelatedDataObjectID不能为空");
                }
                foreach (var dor in dors)
                {
                    assert.IsNotNull(dor.FromDoID, $"{errSummary}的DoR:{dor.ID}的FromDoID不能为空");
                    assert.IsNotNull(dor.ToColumnID, $"{errSummary}的DoR:{dor.ID}的ToColumnID不能为空");
                    assert.IsTrue(fkColumns.Any(col => col.ID == dor.ToColumnID), $"{errSummary},ToColumnID:{dor.ToColumnID}的外键列必须全部存在于DoRs的ToColumnID集合中");
                    assert.IsTrue(fkColumns.Any(col => col.RelatedDataObjectID == dor.FromDoID), $"{errSummary},FromDoID:{dor.FromDoID}的外键列的RelatedDataObjectID必须全部存在于DoRs的FromDOID集合中");
                }
            }


            /**********获取时加入的额外控制***********/
            if (crudType == CRUDType.Retrive)
            {
                assert.IsNotNull(dataObject.ID, $"{errSummary}的系统字段:{nameof(dataObject.ID)}不能为空!");
                assert.IsNotNull(dataObject.Name, $"{errSummary}的系统字段:{nameof(dataObject.Name)}不能为空!");
                assert.IsNotNull(dataObject.Code, $"{errSummary}的系统字段:{nameof(dataObject.Code)}不能为空!");

                foreach (var DoR in dors)
                {
                    assert.IsTrue(DoR.ToDoID == dataObject.ID, $"{errSummary}中的DOR的ToDoID必须等于当前DO.ID");
                    //FromColumnID 暂不校验
                }

                assert.IsNotNull(dataObject.Database, $"{errSummary}所属的数据库:{dataObject.DatabaseID}实例不能为空！");
                if (dataObject.Database != null)
                {
                    //是否需要级联校验DB
                    s_databaseValidator.GetMetadata(dataObject.Database);
                }
            }

            return assert;
        }
    }
}
